package snowflake

import (
	"context"
	"github.com/gogf/gf/v2/container/gmap"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/glog"
	"github.com/gogf/gf/v2/os/gtime"
)

const DefaultName = "default"
const configNodeName = "ys.snowflake"

var (
	// Instances map.
	instances = gmap.NewStrAnyMap(true)
)

// Instance returns an instance of Logger with default settings.
// The parameter `name` is the name for the instance.
func Instance(name ...string) Snowflake {
	key := DefaultName
	if len(name) > 0 && name[0] != "" {
		key = name[0]
	}
	return instances.GetOrSetFuncLock(key, func() interface{} {
		return NewSnowflake(key)
	}).(Snowflake)
}

type Snowflake interface {
	GetId(ctx context.Context) int64
}

type core struct {
	logger *glog.Logger
	
	baseTime     int64
	seq          uint16
	oldTimeStamp int64
	workId       int8
	
	bitLength  int8
	timeOffset int8
	workOffset int8
}

func NewSnowflake(name ...string) Snowflake {
	group := DefaultName
	if len(name) > 0 {
		group = name[0]
	}
	
	logger := g.Log().Clone()
	logger.SetPrefix("snowflake." + group)
	
	workId := FlagWorkId(context.TODO()).GetVar().Int8()
	baseTime := FlagBaseTime(context.TODO()).GetVar().Int64()
	
	var bitLength int8 = 52
	timeOffset := bitLength - 31
	workOffset := timeOffset - 5
	
	return &core{
		logger:       logger,
		baseTime:     baseTime,
		seq:          0,
		oldTimeStamp: 0,
		workId:       workId,
		bitLength:    bitLength,
		timeOffset:   timeOffset,
		workOffset:   workOffset,
	}
}

func (c *core) GetId(ctx context.Context) (id int64) {
	var _timeStamp = gtime.Now().TimestampMilli()/1000 - c.baseTime
	if _timeStamp > c.oldTimeStamp {
		c.oldTimeStamp = _timeStamp
		c.seq = 0
	} else {
		c.seq += 1
		if c.seq > 65530 {
			c.logger.Warning(ctx, "雪花算法满了")
		}
	}
	
	id += _timeStamp << c.timeOffset
	id += int64(c.workId) << c.workOffset
	id += int64(c.seq)
	
	return
}
